package com.caishi.lkx.exam.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.caishi.lkx.common.util.IndexUtil;
import com.caishi.lkx.exam.es.model.QuestionEs;
import com.caishi.lkx.exam.es.service.QuestionEsService;
import com.caishi.lkx.exam.mapper.QuestionExpandMapper;
import com.caishi.lkx.exam.mapper.QuestionMapper;
import com.caishi.lkx.exam.mapper.ScatteredQuestionMapper;
import com.caishi.lkx.exam.model.*;
import com.caishi.lkx.exam.service.*;
import com.caishi.lkx.exam.vo.QuestionStructureVo;
import com.caishi.lkx.exam.vo.QuestionVo;
import com.caishi.lkx.exam.vo.SpecialConditionVo;
import com.zzw.common.Wrappers;
import com.zzw.common.cache.CacheConstants;
import com.zzw.common.entity.IBSMapper;
import com.zzw.common.entity.impl.BSAbsEntity;
import com.zzw.common.entity.impl.BSEntity;
import com.zzw.common.entity.impl.BaseEntity;
import com.zzw.common.exception.BizRuntimeException;
import com.zzw.common.threadpool.SysThreadPool;
import com.zzw.common.utils.CommonUtil;
import com.zzw.common.utils.MD5Util;
import com.zzw.common.utils.UUIDUtil;

import com.caishi.lkx.exam.QuestionCollectionBlockItem;
import com.caishi.lkx.exam.dto.QuestionSimpleDto;
import com.caishi.lkx.exam.dto.QuestionSourceDto;
import com.caishi.lkx.exam.ienum.type.QuestionSourceType;
import com.caishi.lkx.exam.ienum.type.QuestionType;

import com.caishi.lkx.exam.question.QuestionDataProcessComposite;
import com.caishi.lkx.exam.util.QuestionUtil;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author by Jansia
 * @Date 2022/1/10 10:40
 * @Description
 */
@Service
@Slf4j
public class QuestionServiceImpl implements IQuestionService {

    @Resource
    private QuestionMapper questionMapper;

    @Resource
    private QuestionExpandMapper expandMapper;

    @Resource
    private IQuestionTypeService questionTypeService;

    @Resource
    private ICategoryService categoryService;

    @Resource
    private IChapterService chapterService;
    @Resource
    private IPaperService paperService;

    @Resource
    private IIndustryService industryService;

    @Resource
    private ScatteredQuestionMapper scatteredQuestionMapper;
    @Resource
    private IExamService examService;
    @Autowired
    private QuestionExpandMapper questionExpandMapper;
    @Resource
    private QuestionEsService questionEsService;


    @Override
    public IBSMapper<QuestionModel, String> getMapper() {
        return questionMapper;
    }

    private final QuestionDataProcessComposite questionDataProcessComposite = QuestionDataProcessComposite.instance;


    @Override
    public QuestionModel insert(QuestionModel entity) {
        var category = categoryService.getById(entity.getCategoryId());
        return owner(QuestionServiceImpl.class).insert(entity, category.getExamId());
    }


    @Transactional(rollbackFor = Exception.class)
    public QuestionModel insert(QuestionModel entity, String examId) {
        var result = insertProcess(entity, examId);
        if (result == null) return entity;
        Assert.isTrue(expandMapper.insert((QuestionExpandModel) result[1]) == 1);
        return IQuestionService.super.insert((QuestionModel) result[0]);
    }

    public boolean insertBatch(List<QuestionModel> entityList, String examId) {
        var result = entityList.stream().map(v -> this.insertProcess(v, examId)).filter(Objects::nonNull).toList();
        if (result.isEmpty()) return false;
        var r = questionMapper.insertBatch(result.stream().map(v -> (QuestionModel) v[0]).toList());
        Assert.isTrue(expandMapper.insertBatch(result.stream().map(v -> (QuestionExpandModel) v[1]).toList()));
        return r;
    }

    public Object[] insertProcess(QuestionModel entity, String examId) {
        if (entity.getSourceType() == QuestionSourceType.importHand || entity.getSourceType() == QuestionSourceType.inputForA) {
            Assert.isTrue(questionDataProcessComposite.questionContentCheck(entity), "数据不完整:" + entity.getSourceId());
        }
        entity.setId(String.format("%d_%s", examId.hashCode() & 63, UUIDUtil.generateUUIDByTimestamp()));
        var old = getEqualBySign(entity);
        if (old != null) {
            log.warn("试题重复 a={},b={} c={} d={} e={}", entity.getStem(), old.getStem(), old.getAnswer(), old.getAnswer(), examId);
            entity.setId(old.getId());
            return null;
        }
        if (entity.getFirstType() == QuestionType.combination) {
            entity.setId(entity.getId() + "_0");
        } else if (entity.getSourceType() == QuestionSourceType.combination) {
            entity.setId(entity.getId() + "_1");
        }
        entity.setQualityLevel(additionQualityLevel(entity));
        QuestionExpandModel expandModel = new QuestionExpandModel();
        expandModel.setId(entity.getId());
        expandModel.setSign(entity.getSign());
        expandModel.setKnowledgePoint(entity.getKnowledgePoint());
        return new Object[]{entity, expandModel};
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = CacheConstants.SMALL_UP_UP, key = "'answerModel:' + #entity.id")
    public QuestionModel update(QuestionModel entity) {
        Assert.isTrue(questionDataProcessComposite.questionContentCheck(entity), "数据不完整");
        entity.setSign(questionDataProcessComposite.questionContentSign(entity));
        QuestionExpandModel expandModel = new QuestionExpandModel();
        expandModel.setId(entity.getId());
        expandModel.setSign(entity.getSign());
        expandModel.setKnowledgePoint(entity.getKnowledgePoint());
        expandMapper.updateById(expandModel);
        return IQuestionService.super.update(entity);
    }

    @Override
    public boolean update(QuestionModel entity, Wrapper<QuestionModel> updateWrapper) {
        throw new BizRuntimeException();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(List<String> strings) {
        return IQuestionService.super.delete(strings) && expandMapper.deleteBatchIds(strings) == strings.size();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = CacheConstants.SMALL_UP_UP, key = "'answerModel:' + #s")
    public Boolean delete(String s) {
        return IQuestionService.super.delete(s) && expandMapper.deleteById(s) == 1;
    }

    @Override
    public int delete(Wrapper<QuestionModel> queryWrapper) {
        throw new BizRuntimeException();
    }

    @Override
    public BigDecimal additionQualityLevel(QuestionModel questionModel) {
        if (questionModel.getFirstType() == null) {
            if (StrUtil.isEmpty(questionModel.getId())) {
                return BigDecimal.ZERO;
            }
            QuestionModel old = questionMapper.selectOne(Wrappers.<QuestionModel>lambdaQuery()
                    .select(QuestionModel::getFirstType)
                    .eq(BSAbsEntity::getId, questionModel.getId()));

            questionModel.setFirstType(old.getFirstType());
        }
        return BigDecimal.valueOf(questionDataProcessComposite.computedQualityLevel(questionModel));
    }

    @SneakyThrows
    @Override
    public QuestionModel getQuestionById(String resourceId) {
        Future<List<QuestionModel>> childrenFuture = null;
        if (QuestionUtil.questionIdIsCombinationParent(resourceId)) {
            childrenFuture = SysThreadPool.submit(() -> questionMapper.selectList(
                    Wrappers.<QuestionModel>lambdaQuery()
                            .eq(QuestionModel::getSourceId, resourceId)
                            .eq(QuestionModel::getSourceType, QuestionSourceType.combination)
                            .eq(QuestionModel::getIsHide, false)
            ));
        }
        var model = questionMapper.selectOne(Wrappers.<QuestionModel>lambdaQuery().eq(BSAbsEntity::getId, resourceId));
        if (model != null) {
            model.setChildren(childrenFuture == null ? null : childrenFuture.get());
        }
        return model;
    }


    @Override
    public List<QuestionModel> getCombineList(String id) {
        return questionMapper.selectList(Wrappers.<QuestionModel>lambdaQuery()
                .eq(QuestionModel::getSourceType, QuestionSourceType.combination)
                .eq(QuestionModel::getSourceId, id).orderByAsc(QuestionModel::getId));
    }

    public Boolean isHideQuestion(List<String> ids, Boolean isHide) {
        return questionMapper.update(null, Wrappers.lambdaUpdate(new QuestionModel()).set(QuestionModel::getIsHide, isHide).in(QuestionModel::getId, ids)) > 0;
    }

    @SneakyThrows
    @Override
    public QuestionSimpleDto questionSimpleData(String qid) {
        Future<List<QuestionModel>> childrenFuture = null;
        var wrapper = simpleDataWrapper();
        if (QuestionUtil.questionIdIsCombinationParent(qid)) {
            childrenFuture = SysThreadPool.submit(() -> questionMapper.selectList(
                    wrapper.eq(QuestionModel::getSourceId, qid)
                            .eq(QuestionModel::getSourceType, QuestionSourceType.combination)
                            .eq(QuestionModel::getIsHide, false)
            ));
        }
        var model = questionMapper.selectOne(
                wrapper.eq(BSEntity::getId, qid)
                        .eq(QuestionModel::getIsHide, false)
        );
        model.setChildren(childrenFuture == null ? null : childrenFuture.get());
        return QuestionSimpleDto.modelTrans(model);
    }

    @SneakyThrows
    @Override
    public QuestionVo clientGetQuestionById(String id) {
        Future<List<QuestionVo>> childrenFuture = null;
        if (QuestionUtil.questionIdIsCombinationParent(id)) {
            String finalId = id;
            childrenFuture = SysThreadPool.submit(() -> questionMapper.selectList(
                    simpleDataWrapper()
                            .eq(QuestionModel::getSourceId, finalId)
                            .eq(QuestionModel::getSourceType, QuestionSourceType.combination)
                            .eq(QuestionModel::getIsHide, false)
            ).stream().map(v -> {
                var vo = QuestionVo.modelTrans(v);
                vo.setType(questionTypeName(v.getTypeId(), v.getFirstType()));
                return vo;
            }).toList());
        } else if (QuestionUtil.questionIdIsCombinationChild(id)) {
            QuestionModel questionModel = questionMapper.selectById(id);
            QuestionModel parentModel = questionMapper.selectOne(simpleDataWrapper().eq(QuestionModel::getId, questionModel.getSourceId()));
            id = parentModel.getId();
            childrenFuture = SysThreadPool.submit(() -> questionMapper.selectList(
                    simpleDataWrapper()
                            .eq(QuestionModel::getSourceId, parentModel.getId())
                            .eq(QuestionModel::getSourceType, QuestionSourceType.combination)
                            .eq(QuestionModel::getIsHide, false)
            ).stream().map(v -> {
                var vo = QuestionVo.modelTrans(v);
                vo.setType(questionTypeName(v.getTypeId(), v.getFirstType()));
                return vo;
            }).toList());

        }
        var m = questionMapper.selectOne(simpleDataWrapper().eq(BSAbsEntity::getId, id));
        var expandModel = questionExpandMapper.selectOne(Wrappers.<QuestionExpandModel>lambdaQuery().eq(BSAbsEntity::getId, id));
        if (m == null) return null;
        var model = QuestionVo.modelTrans(m);
        model.setDoingNumber(expandModel.getDoingNumber());
        model.setErrorNumber(expandModel.getErrorNumber());

        model.setType(questionTypeName(m.getTypeId(), m.getFirstType()));
        model.setChildren(childrenFuture == null ? null : childrenFuture.get());
        return model;
    }

    @Override
    public List<QuestionVo> clientQuestionCollection(List<String> ids, String industryId, String examId, Long categoryId) {
        return clientQuestionCollection(ids, industryId, examId, categoryId, true);
    }
    List<QuestionModel> selectList(List<String> ids){

        List<QuestionEs> esList = null;
        try {
            esList = questionEsService.getByIds(ids);
        } catch (Exception e) {
            esList = questionEsService.getByIds(ids);
        }


        // todo 有问题，有单条数据在es，导致其他的试题数据一直没有同步到es
        if(esList.size()<ids.size()){
           List<QuestionModel> list =  selectList(simpleDataWrapper()
                    .in(BSEntity::getId, ids).eq(QuestionModel::getIsHide, false));
            questionEsService.batchUpdate(list.stream().map(QuestionEs::modelTrans).toList());
            return list;
        }
        return esList.stream().map(QuestionEs::modelTransToModel).toList();
    };
    @Override
    public List<QuestionVo> clientQuestionCollection(List<String> ids, String industryId, String examId, Long categoryId, boolean type) {
        if (CollUtil.isEmpty(ids)) return Collections.emptyList();

        var models = this.selectList(ids);

        List<QuestionExpandModel> expandModels = questionExpandMapper.selectList(Wrappers.<QuestionExpandModel>lambdaQuery()
                .in(QuestionExpandModel::getId, ids));
        // 单独处理复合题
        Map<String, QuestionModel> cache = new LinkedHashMap<>();
        var combinationQuestions = models.stream().map(v -> {
            if (QuestionUtil.questionIdIsCombinationParent(v.getId())) return v;
            if (QuestionUtil.questionIdIsCombinationChild(v.getId()) && !cache.containsKey(v.getSourceId())) {
                var r = getById(v.getSourceId());
                cache.put(r.getId(), r);
                return r;
            }
            return null;
        }).filter(Objects::nonNull).toList();

        Map<String, QuestionVo> result = new LinkedHashMap<>();
        if (CollUtil.isNotEmpty(combinationQuestions)) {
            combinationQuestions.parallelStream().forEach(v -> {

                var vo = QuestionVo.modelTrans(v);
                expandModels.parallelStream().filter(e -> e.getId().equals(vo.getId())).forEach(e -> {
                    vo.setDoingNumber(e.getDoingNumber());
                    vo.setErrorNumber(e.getErrorNumber());
                });
                if (type) {
                    vo.setType(questionTypeName(v.getTypeId(), vo.getFirstType()));
                }
                vo.setChildren(questionMapper.selectList(simpleDataWrapper()
                                .eq(QuestionModel::getSourceId, vo.getId())
                                .eq(QuestionModel::getSourceType, QuestionSourceType.combination)
                                .eq(QuestionModel::getIsHide, false))
                        .stream().map(m -> {
                            var vo1 = QuestionVo.modelTrans(m);
                            if (!type) return vo1;
                            vo1.setType(questionTypeName(m.getTypeId(), m.getFirstType()));
                            return vo1;
                        }).toList()
                );
                synchronized (result) {
                    result.put(vo.getId(), vo);
                }
            });
        }
        // 处理其他类型试题
        {
            models.forEach(v -> {
                if (QuestionUtil.questionIdIsCombination(v.getId())) return;
                var vo = QuestionVo.modelTrans(v);
                expandModels.parallelStream().filter(e -> e.getId().equals(v.getId())).forEach(e -> {
                    vo.setDoingNumber(e.getDoingNumber());
                    vo.setErrorNumber(e.getErrorNumber());
                });
                if (type) {
                    vo.setType(questionTypeName(v.getTypeId(), vo.getFirstType()));
                }
                result.put(vo.getId(), vo);
            });
        }
        return ids.stream().map(result::get).filter(Objects::nonNull).toList();
    }

    @Override
    public List<QuestionStructureVo> questionCollectionStructure(List<String> ids, String industryId, String examId, Long categoryId) {
        var query = Wrappers.<QuestionModel>lambdaQuery().select(QuestionModel::getId, QuestionModel::getFirstType, QuestionModel::getTypeId);
        var models = selectList(query.in(BSEntity::getId, ids).eq(QuestionModel::getIsHide, false));
        return models.parallelStream().map(v -> {
            var vo = new QuestionStructureVo();
            vo.setId(v.getId());
            vo.setFirstType(v.getFirstType());
            vo.setType(questionTypeName(v.getTypeId(), v.getFirstType()));
            if (QuestionUtil.questionIdIsCombinationParent(v.getId())) {
                vo.setChildren(questionMapper.selectList(query.clone()
                                .eq(QuestionModel::getSourceId, v.getId())
                                .eq(QuestionModel::getSourceType, QuestionSourceType.combination)
                                .eq(QuestionModel::getIsHide, false))
                        .stream().map(v1 -> {
                            var vo1 = new QuestionStructureVo();
                            vo1.setId(v1.getId());
                            vo1.setFirstType(v1.getFirstType());
                            vo1.setType(questionTypeName(v1.getTypeId(), v1.getFirstType()));
                            return vo1;
                        }).toList()
                );
            }
            return vo;
        }).toList();
    }

    @Override
    @Cacheable(value = CacheConstants.SMALL_UP_UP, key = "'answerModel:' + #id", unless = "#result == null")
    public QuestionModel answerModel(String id) {
        return questionMapper.selectOne(wrappers().select(QuestionModel::getRightAnswerStr).eq(BSEntity::getId, id));
    }

    @Override
    public Object answerDecrypt(String base64, String userId, String collectId) {
        String md5 = MD5Util.MD5Encode(userId + collectId);
        var aes = SecureUtil.aes(md5.getBytes(StandardCharsets.UTF_8));
        return JSON.parse(aes.decryptStr(base64));
    }

    public static void main(String[] args) {

        Object o =JSON.parse("[\"value1\", \"value2\"]");

            System.out.println(o.getClass());
            QuestionServiceImpl q = new QuestionServiceImpl();
            System.out.println(q.answerDecrypt("4kHiFUoMfgZSJoa0J/ZbTA==","1821847822305005568","1819214850212958208"));
    }

    @Override
    public String answerEncrypt(Object answer, String userId, String collectId) {
        String md5 = MD5Util.MD5Encode(userId + collectId);
        var aes = SecureUtil.aes(md5.getBytes(StandardCharsets.UTF_8));
        return aes.encryptBase64(JSON.toJSONString(answer));
    }

    private boolean filterFunc(List<SpecialConditionVo.TY> types, SpecialConditionVo.TY type, QuestionModel q) {
        var tid = type.getTypeId();
        var tt = type.getType();
        if (StrUtil.isBlank(tid)) {
            if (types.stream().anyMatch(v1 -> v1.getType() == tt && StrUtil.isNotEmpty(v1.getTypeId()))) {
                return q.getFirstType() == tt && StrUtil.isBlank(q.getTypeId());
            }
            return q.getFirstType() == tt;
        }
        return tid.equals(q.getTypeId());
    }

    @Override
    public List<QuestionCollectionBlockItem> generateQuestionBlock(Long categoryId, List<SpecialConditionVo.TY> types, Integer count, List<String> inIds,
                                                                   Function<QuestionCollectionBlockItem, String> descCall, Consumer<BigDecimal> allScoreCall) {
        if (types == null) types = Collections.emptyList();
        var filterFirstType = types.stream().filter(v -> v.getTypeId() == null).map(SpecialConditionVo.TY::getType).toList();
        var filterTypeId = types.stream().map(SpecialConditionVo.TY::getTypeId).filter(Objects::nonNull).toList();
        if (CollUtil.isEmpty(inIds)) {
            inIds = categoryService.categoryRelationQuestionIds(categoryId);
        }
        if (CollUtil.isEmpty(inIds)) return new LinkedList<>();
        List<List<String>> split = IndexUtil.splitList(inIds);
        List<SpecialConditionVo.TY> finalTypes1 = types;
        var allQuestion = split.parallelStream().map(v -> selectList(wrappers()
                .select(QuestionModel::getId, QuestionModel::getTypeId, QuestionModel::getFirstType, QuestionModel::getScore)
                .in(BSEntity::getId, v)
                .ne(QuestionModel::getSourceType, QuestionSourceType.combination)
                .and(CollUtil.isNotEmpty(finalTypes1), w -> w
                        .in(CollUtil.isNotEmpty(filterFirstType), QuestionModel::getFirstType, filterFirstType)
                        .or()
                        .in(CollUtil.isNotEmpty(filterTypeId), QuestionModel::getTypeId, filterTypeId)
                )
        )).flatMap(List::stream).toList();

        List<QuestionModel> resultQuestion;
        List<SpecialConditionVo.TY> finalTypes = types;
        if (!types.isEmpty() && types.stream().anyMatch(v -> v.getCount() != null && v.getCount() > 0)) {
            resultQuestion = new LinkedList<>();
            types.forEach(v -> {
                var allList = allQuestion.stream()
                        .filter(q -> filterFunc(finalTypes, v, q))
                        .toList();
                resultQuestion.addAll(CommonUtil.randomEleList(allList, Math.min(v.getCount(), 200)));
            });
        } else if (count != null) {
            resultQuestion = CommonUtil.randomEleList(allQuestion, Math.min(500, count));
        } else {
            resultQuestion = allQuestion;
        }
        if (resultQuestion.isEmpty()) {
            throw new BizRuntimeException("没有可组合的试题");
        }
        var blockSort = new AtomicInteger();
        var allScore = new AtomicReference<>(BigDecimal.ZERO);
        if (types.isEmpty()) {
            var k = allQuestion.stream().collect(Collectors.groupingBy(QuestionModel::getTypeId, Collectors.mapping(QuestionModel::getFirstType, Collectors.toSet())));
            types = k.entrySet().stream().flatMap(v -> v.getValue().stream().map(v1 -> {
                var i = new SpecialConditionVo.TY();
                i.setTypeId(v.getKey());
                i.setType(v1);
                i.setName(questionTypeService.queryName(v.getKey(), v1));
                return i;
            })).sorted(Comparator.comparing(s -> s.getType().getCode())).toList();
        }
        var result = types.stream().map(v -> {
            var blockItem = new QuestionCollectionBlockItem();
            blockItem.setSort(blockSort.getAndIncrement());
            blockItem.setTyName(v.getName());
            if (descCall != null) {
                blockItem.setDesc(descCall.apply(blockItem));
            }
            var itemSort = new AtomicInteger();
            var score = new AtomicReference<>(BigDecimal.ZERO);
            blockItem.setQs(resultQuestion.stream()
                    .filter(q -> filterFunc(finalTypes, v, q))
                    .map(q -> {
                        var item = new QuestionCollectionBlockItem.QuestionCollectionItem();
                        item.setId(q.getId());
                        item.setScore(q.getScore() == null ? BigDecimal.ONE : q.getScore());
                        item.setSort(itemSort.getAndIncrement());
                        score.set(score.get().add(item.getScore()));
                        return item;
                    })
                    .toList()
            );
            blockItem.setScore(score.get());
            allScore.set(allScore.get().add(blockItem.getScore()));
            return blockItem;
        }).toList();
        if (allScoreCall != null) {
            allScoreCall.accept(allScore.get());
        }
        return result;
    }

    @Override
    public QuestionSourceDto questionSource(String questionId) {
        QuestionModel questionModel = this.getById(questionId);
        if (null != questionModel) {
            QuestionSourceDto sourceDto = new QuestionSourceDto();
            List<QuestionSourceDto.ChapterPath> chapterList = new ArrayList<>();
            List<ChapterModel> models = this.chapterService.chapterByQuestionId(questionModel.getCategoryId(), questionId);
            if (!models.isEmpty()) {
                models.parallelStream().forEach(f -> {
                    List<ChapterModel> chapterModels = this.chapterPath(f);
                    List<String> chapterName = new ArrayList<>();
                    if (!chapterModels.isEmpty()) {
                        chapterName = chapterModels.stream().map(ChapterModel::getName).toList();
                    }
                    QuestionSourceDto.ChapterPath chapterPath = new QuestionSourceDto.ChapterPath();
                    chapterPath.setChapterName(chapterName);
                    Long categoryId = f.getCategoryId();
                    CategoryModel model = categoryService.getById(categoryId);
                    chapterPath.setIndustryName(industryService.queryName(model.getIndustryId()));
                    chapterPath.setExamName(examService.queryName(model.getExamId()));
                    chapterPath.setCategoryName(categoryService.categoryByLeafPath(categoryId));
                    chapterList.add(chapterPath);
                });
            }
            List<QuestionSourceDto.PaperPath> paperPathList = new ArrayList<>();
            this.paperService.paperByQuestionId(questionModel.getCategoryId(), questionId).parallelStream().forEach(f -> {
                QuestionSourceDto.PaperPath paperPath = new QuestionSourceDto.PaperPath();
                paperPath.setPaperName(f.getName());
                Long categoryId = f.getCategoryId();
                CategoryModel model = categoryService.getById(categoryId);
                paperPath.setExamName(examService.queryName(model.getExamId()));
                paperPath.setCategoryName(categoryService.categoryByLeafPath(f.getCategoryId()));
                paperPath.setIndustryName(industryService.queryName(model.getIndustryId()));
                paperPathList.add(paperPath);
            });
            sourceDto.setChapterList(chapterList);
            sourceDto.setPaperList(paperPathList);
            return sourceDto;
        }
        return null;
    }

    @Override
    public List<String> getScatteredQuestion(Long categoryId, Boolean isUse) {
        return scatteredQuestionMapper.selectList(Wrappers.<ScatteredQuestionModel>lambdaQuery().eq(ScatteredQuestionModel::getCategoryId, categoryId).eq(null != isUse, ScatteredQuestionModel::getIsUse, isUse).select(ScatteredQuestionModel::getQuestionId)).parallelStream().map(ScatteredQuestionModel::getQuestionId).collect(Collectors.toList());
    }

    //根据子章节获取整个章节的路径
    private List<ChapterModel> chapterPath(ChapterModel chapterModel) {
        List<ChapterModel> list = new ArrayList<>();
        list.add(chapterModel);
        while (chapterModel != null && !"0".equals(chapterModel.getParentId())) {
            ChapterModel model = chapterService.getById(chapterModel.getParentId());
            if (model != null) {
                list.add(0, model);
            }
            chapterModel = model;
        }
        return list;
    }


    @Override
    public String questionTypeName(String typeId, QuestionType questionType) {
        return questionTypeService.queryName(typeId, questionType);
    }

    private LambdaQueryWrapper<QuestionModel> simpleDataWrapper() {
        return Wrappers.<QuestionModel>lambdaQuery()
                .select(
                        QuestionModel::getId,
                        QuestionModel::getFirstType,
                        QuestionModel::getTypeId,
                        QuestionModel::getDifficulty,
                        QuestionModel::getStem,
                        QuestionModel::getQuOption,
                        QuestionModel::getAnswerDesc,
                        QuestionModel::getRightAnswerStr,
                        QuestionModel::getSourceId,
                        QuestionModel::getSourceType,
                        QuestionModel::getCategoryId,
                        QuestionModel::getIsHide,
                        QuestionModel::getScore,
                        QuestionModel::getKnowledgeLevel,
                        QuestionModel::getQualityLevel
                );
    }

    private QuestionModel getEqualBySign(QuestionModel entity) {
        entity.setSign(questionDataProcessComposite.questionContentSign(entity));
        var list = expandMapper.selectList(Wrappers.<QuestionExpandModel>lambdaQuery().eq(QuestionExpandModel::getSign, entity.getSign()));
        if (CollUtil.isEmpty(list)) return null;
        var qs = selectBatchIds(list.stream().map(BaseEntity::getId).toList());
        for (var q : qs) {
            q.setAnswer(q.getAnswer());
            if (questionEqual(q, entity)) {
                return q;
            }
        }
        return null;
    }

    public boolean questionEqual(QuestionModel a, QuestionModel b) {
        if (a == null || b == null) return false;
        if (a == b || a.getId() != null && a.getId().equals(b.getId())) return true;
        if (a.getFirstType() != b.getFirstType()) return false;
        var stem = a.getStem();
        var options = a.getQuOption();
        var answer = a.getRightAnswerStr();
        var stem1 = b.getStem();
        var options1 = b.getQuOption();
        var answer1 = b.getRightAnswerStr();
        var r = stem == stem1 || stem != null && stem1 != null && JSON.toJSONString(stem).equals(JSON.toJSONString(stem1));
        r = r && options == options1 || options != null && options1 != null && JSON.toJSONString(options).equals(JSON.toJSONString(options1));
        r = r && Objects.equals(answer, answer1);
        if (r && (a.getFirstType() == QuestionType.combination || a.getSourceType() == QuestionSourceType.combination)) {
            r = a.getSourceId().equals(b.getSourceId());
        }
        return r;
    }

//    public QuestionEs convertQuestionEs(QuestionModel questionModel) {
//        CategoryModel categoryModel = categoryService.getById(questionModel.getCategoryId());
//        QuestionEs questionEs = new QuestionEs();
//        questionEs.setId(questionModel.getId());
//        questionEs.setCategoryId(questionModel.getCategoryId());
//        questionEs.setAnswer(questionModel.getAnswer());
//        questionEs.setExamId(categoryModel.getExamId());
//        questionEs.setStem(questionModel.getStem());
//        questionEs.setQuOption(questionModel.getQuOption());
//        questionEs.setAnswerDesc(questionModel.getAnswerDesc());
//        questionEs.setIndustryId(categoryModel.getIndustryId());
//        return questionEs;
//    }

}
